﻿namespace CL_Searcher
{    
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Net;
    using System.Net.Mail;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Threading;
    using System.Xml;
    using CL_Searcher.Classes;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Configuration config = new Configuration(new ObservableCollection<SearchQuery>(), new SearchQuery(), new ObservableCollection<Country>(), new ObservableCollection<Category>(), new ObservableCollection<City>(), new Classes.Settings("server.com", "25", true, "username", "Password", "from@email.com", "to@email.com", false), new List<string>());
        private DispatcherTimer timer = new DispatcherTimer();

        public MainWindow()
        {
            InitializeComponent();
            CheckForUpdates();
            
            // Add the version to the title
            this.Title = this.Title + " (" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + ")";           
            
            AddCLData();
            LoadSettings();
            this.DataContext = config;
            Grid_setSearchQueries();

            // Stops mass emails when the application is restarted
            foreach (SearchQuery item in config.SearchQueries)
            {
                item.LastSearchTime = DateTime.Now;
            }

            // Configure Timer, runs ever 30 minutes, CL Feeds update at +1h intervals.
            timer.Interval = TimeSpan.FromMinutes(30);
            timer.Tick += new EventHandler(Timer_RefreshData);
            timer.Start();
        }

        #region Craigslist Parsing

        /// <summary>
        /// Checks if Posts is new and triggers an alert. Adds Posts to the SearchItem
        /// </summary>
        /// <param name="searchItem"></param>
        /// <param name="item"></param>
        private void AddItem(SearchQuery searchItem, Post item)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(
            System.Windows.Threading.DispatcherPriority.Normal,
            (Action)delegate()
            {
                if (!searchItem.Posts.Any(x => x.Link == item.Link && x.Date == item.Date) && !config.HiddenPosts.Contains(item.Link))
                {
                    if (DateTime.Compare(searchItem.LastSearchTime, item.Date) < 0)
                    {
                        // New Item
                        searchItem.Posts.Add(item);
                        if (config.Settings.EmailNewPosts)
                        {
                            MailAlert(item);
                        }
                    }
                    else
                    {
                        searchItem.Posts.Add(item);
                    }
                }
            });
        }

        /// <summary>
        /// Builds the URL based on the SearchItem, Location and index
        /// </summary>
        /// <param name="searchItem"></param>
        /// <param name="location"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private string BuildQueryStrings(SearchQuery searchItem, City location, int index)
        {
            string query = location.Url;
            query += "/search/?";
            query += "query=" + System.Net.WebUtility.HtmlEncode(searchItem.Query.Replace(" ", "+"));
            query += "&catAbb=" + searchItem.Category.Code;
            query += "&minAsk=" + searchItem.MinPrice;
            query += "&maxAsk=" + searchItem.MaxPrice;

            if (searchItem.TitleOnly)
            {
                query += "&srchType=T";
            }
            else
            {
                query += "&srchType=A";
            }

            if (searchItem.HasImage)
            {
                query += "&hasPic=1";
            }
            else
            {
                query += "&hasPic=0";
            }

            query += "&s=" + (index * 25).ToString();

            query += "&format=rss";

            return query;
        }

        /// <summary>
        /// Get the child node value
        /// </summary>
        private string GetChildNodeValue(XmlNode node, string name)
        {
            foreach (XmlNode childNode in node.ChildNodes)
            {
                if (childNode.Name == name)
                {
                    return childNode.InnerText;
                }
            }

            return string.Empty;
        }

        /// <summary>
        /// Parses the XML and grabs the required data
        /// </summary>
        /// <param name="searchItem"></param>
        /// <param name="nodes"></param>
        private void ParseNodes(SearchQuery searchItem, XmlNodeList nodes)
        {
            foreach (XmlNode node in nodes)
            {
                Post item = new Post();
                item.Title = System.Net.WebUtility.HtmlDecode(GetChildNodeValue(node, "title"));
                item.Link = GetChildNodeValue(node, "link");
                item.Description = GetChildNodeValue(node, "description");
                string dateString = GetChildNodeValue(node, "dc:date");
                if (dateString != string.Empty)
                {
                    DateTime date;
                    if (DateTime.TryParse(dateString, out date))
                    {
                        item.Date = date;
                    }
                }

                if (item.Title.Contains("$"))
                {
                    string priceString = item.Title.Substring(item.Title.LastIndexOf("$") + 1);
                    int price;
                    if (int.TryParse(priceString, out price))
                    {
                        item.Price = price;
                    }
                }

                AddItem(searchItem, item);
            }
        }

        /// <summary>
        /// Begins the Query on the SearchItem
        /// </summary>
        /// <param name="searchItem"></param>
        private void RunQuery(SearchQuery searchItem)
        {
            var parent = Task.Factory.StartNew(() =>
            {
                foreach (City location in searchItem.Locations)
                {
                    var child = Task.Factory.StartNew(() =>
                    {
                        bool keepLooping = true;
                        int i = 0;
                        int errors = 0;
                        while (keepLooping)
                        {
                            try
                            {
                                XmlDocument feedXML = new XmlDocument();
                                feedXML.Load(BuildQueryStrings(searchItem, location, i));

                                XmlNodeList nodes = feedXML.GetElementsByTagName("item");
                                if (nodes.Count != 0)
                                {
                                    ParseNodes(searchItem, nodes);
                                }
                                else
                                {
                                    keepLooping = false;
                                }

                                i++;
                            }
                            catch
                            {
                                if (errors > 4)
                                {
                                    keepLooping = false;
                                }
                                errors++;
                            }
                        }
                    }, TaskCreationOptions.AttachedToParent);
                }
            });

            parent.Wait();

            if (searchItem.Posts.Count > 0)
            {
                searchItem.LastSearchTime = ((Post)searchItem.Posts.OrderBy(item => item.Date).Last()).Date;
            }
        }

        /// <summary>
        /// Re runs all of the queries
        /// </summary>
        private void RunAllQueries()
        {
            foreach (SearchQuery item in config.SearchQueries)
            {
                var task = Task.Factory.StartNew(() =>
                {
                    RunQuery(item);
                });
            }

            txt_StatusBar.Content = "Last updated: " + DateTime.Now.ToString();
        }

        #endregion Craigslist Parsing

        #region Buttons
        private void Btn_AddLocation_Click(object sender, RoutedEventArgs e)
        {
            if (dd_City.SelectedItem != null)
            {
                City city = (City)dd_City.SelectedItem;

                if (!config.SelectedLocations.Contains(city))
                {
                    config.SelectedLocations.Add(city);
                }
            }
        }

        private void Btn_Refresh_Click(object sender, RoutedEventArgs e)
        {
            RunAllQueries();
        }

        private void Btn_UnhideAll_Click(object sender, RoutedEventArgs e)
        {
            config.HiddenPosts.Clear();
            RunAllQueries();
        }
        
        private void Btn_RemoveLocation_Click(object sender, RoutedEventArgs e)
        {
            if (lst_Locations.SelectedIndex != -1)
            {
                config.SelectedLocations.RemoveAt(lst_Locations.SelectedIndex);
            }
        }

        private void Btn_SearchQueryAdd_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(txt_Query.Text))
            {
                MessageBox.Show("Query is missing.", "Warning");
                return;
            }

            if (config.SelectedLocations.Count == 0)
            {
                MessageBox.Show("Add some locations to query.", "Warning");
                return;
            }

            if (dd_Category.SelectedIndex == -1)
            {
                MessageBox.Show("Select a category.", "Warning");
                return;
            }

            int minPrice = 0;
            int maxPrice = 0;

            if (!string.IsNullOrEmpty(txt_MinPrice.Text))
            {
                bool result = int.TryParse(txt_MinPrice.Text, out minPrice);
                if (!result)
                {
                    MessageBox.Show("The Min Price has to be a number with no decimals.", "Warning");
                    return;
                }
            }

            if (!string.IsNullOrEmpty(txt_MaxPrice.Text))
            {
                bool result = int.TryParse(txt_MaxPrice.Text, out maxPrice);
                if (!result)
                {
                    MessageBox.Show("The Max Price has to be a number with no decimals.");
                    return;
                }
            }

            if (minPrice > maxPrice)
            {
                MessageBox.Show("The Max Price has to be higher than the Min Price.");
                return;
            }

            SearchQuery si = new SearchQuery(txt_Query.Text, (Category)dd_Category.SelectedItem, dd_Category.SelectedValue.ToString(), minPrice, maxPrice, (bool)ckb_HasImage.IsChecked, (bool)ckb_TitleOnly.IsChecked, config.SelectedLocations, DateTime.Now, new ObservableCollection<Post>());
            if (config.SearchQueries.Where(x => x.Query == si.Query && x.HasImage == si.HasImage && x.Locations == si.Locations).Any())
            {
                MessageBox.Show("Similar query already exists.");
                return;
            }

            config.SearchQueries.Add(si);
            var task = Task.Factory.StartNew(() =>
            {
                RunQuery(config.SearchQueries[config.SearchQueries.Count - 1]);
            });
        }
        #endregion Buttons

        #region Grid

        private void BackMenuItem_Click(object sender, RoutedEventArgs e)
        {
            Grid_setSearchQueries();
        }

        private void DeleteMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (grid.SelectedIndex != -1)
            {
                config.SearchQueries.RemoveAt(grid.SelectedIndex);
            }
        }

        private void EditMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (grid.SelectedIndex != -1)
            {
                txt_Query.Text = config.SearchQueries[grid.SelectedIndex].Query;
                dd_Category.SelectedValue = config.SearchQueries[grid.SelectedIndex].Category.Description;
                txt_MinPrice.Text = Convert.ToString(config.SearchQueries[grid.SelectedIndex].MinPrice);
                txt_MaxPrice.Text = Convert.ToString(config.SearchQueries[grid.SelectedIndex].MaxPrice);
                ckb_HasImage.IsChecked = config.SearchQueries[grid.SelectedIndex].HasImage;
                ckb_TitleOnly.IsChecked = config.SearchQueries[grid.SelectedIndex].TitleOnly;
                config.SelectedLocations = config.SearchQueries[grid.SelectedIndex].Locations;
                lst_Locations.ItemsSource = config.SelectedLocations;
                config.SearchQueries.RemoveAt(grid.SelectedIndex);
            }
        }

        private void Grid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (grid.SelectedIndex != -1)
            {
                if (grid.ItemsSource == config.SearchQueries)
                {
                    Grid_setPost();
                }
                else
                {
                    Post item = (Post)grid.SelectedItem;
                    System.Diagnostics.Process.Start(item.Link);
                }
            }
        }

        private void Grid_setPost()
        {
            config.SelectedSearchQuery = config.SearchQueries[grid.SelectedIndex];
            grid.ItemsSource = config.SelectedSearchQuery.Posts.OrderByDescending(x => x.Date);

            // Create menu item.
            MenuItem backMenuItem = new MenuItem();
            backMenuItem.Header = "Back";
            backMenuItem.Click += new RoutedEventHandler(BackMenuItem_Click);

            // Create menu item.
            MenuItem hideMenuItem = new MenuItem();
            hideMenuItem.Header = "Hide Post";
            hideMenuItem.Click += new RoutedEventHandler(HideMenuItem_Click);

            // Create contextual menu.
            ContextMenu contextMenu = new ContextMenu();
            contextMenu.Items.Add(backMenuItem);
            contextMenu.Items.Add(hideMenuItem);

            // Attach context-menu to grid.
            grid.ContextMenu = contextMenu;

            grid.Columns.Clear();
            DataGridTextColumn titleColumn = new DataGridTextColumn();
            titleColumn.Header = "Title";
            titleColumn.Binding = new Binding("Title");
            grid.Columns.Add(titleColumn);

            DataGridTextColumn linkColumn = new DataGridTextColumn();
            linkColumn.Header = "Link";
            linkColumn.Binding = new Binding("Link");
            grid.Columns.Add(linkColumn);

            DataGridTextColumn priceColumn = new DataGridTextColumn();
            priceColumn.Header = "Price";
            priceColumn.Binding = new Binding("Price");
            grid.Columns.Add(priceColumn);

            DataGridTextColumn dateColumn = new DataGridTextColumn();
            dateColumn.Header = "Date";
            dateColumn.Binding = new Binding("Date");
            grid.Columns.Add(dateColumn);
        }

        private void Grid_setSearchQueries()
        {
            grid.ItemsSource = config.SearchQueries;

            // Create menu item.
            MenuItem editMenuItem = new MenuItem();
            editMenuItem.Header = "Edit";
            editMenuItem.Click += new RoutedEventHandler(EditMenuItem_Click);

            MenuItem deleteMenuItem = new MenuItem();
            deleteMenuItem.Header = "Delete";
            deleteMenuItem.Click += new RoutedEventHandler(DeleteMenuItem_Click);

            // Create contextual menu.
            ContextMenu contextMenu = new ContextMenu();
            contextMenu.Items.Add(editMenuItem);
            contextMenu.Items.Add(deleteMenuItem);

            // Attach context-menu to grid.
            grid.ContextMenu = contextMenu;

            grid.Columns.Clear();
            DataGridTextColumn queryColumn = new DataGridTextColumn();
            queryColumn.Header = "Query";
            queryColumn.Binding = new Binding("Query");
            grid.Columns.Add(queryColumn);

            DataGridTextColumn categoryColumn = new DataGridTextColumn();
            categoryColumn.Header = "Category";
            categoryColumn.Binding = new Binding("CategoryDescription");
            grid.Columns.Add(categoryColumn);

            DataGridTextColumn minPriceColumn = new DataGridTextColumn();
            minPriceColumn.Header = "Min Price";
            minPriceColumn.Binding = new Binding("MinPrice");
            grid.Columns.Add(minPriceColumn);

            DataGridTextColumn maxPriceColumn = new DataGridTextColumn();
            maxPriceColumn.Header = "Max Price";
            maxPriceColumn.Binding = new Binding("MaxPrice");
            grid.Columns.Add(maxPriceColumn);

            DataGridCheckBoxColumn hasImageColumn = new DataGridCheckBoxColumn();
            hasImageColumn.Header = "Has Image";
            hasImageColumn.Binding = new Binding("HasImage");
            grid.Columns.Add(hasImageColumn);

            DataGridCheckBoxColumn titleOnlyColumn = new DataGridCheckBoxColumn();
            titleOnlyColumn.Header = "Title Only";
            titleOnlyColumn.Binding = new Binding("TitleOnly");
            grid.Columns.Add(titleOnlyColumn);

            DataGridTextColumn itemCountColumn = new DataGridTextColumn();
            itemCountColumn.Header = "Posts";
            itemCountColumn.Binding = new Binding("Posts.Count");
            grid.Columns.Add(itemCountColumn);

            DataGridTextColumn locationCountColumn = new DataGridTextColumn();
            locationCountColumn.Header = "Locations";
            locationCountColumn.Binding = new Binding("Locations.Count");
            grid.Columns.Add(locationCountColumn);
        }

        private void HideMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (grid.SelectedIndex != -1)
            {
                if (!config.HiddenPosts.Contains(((Post)grid.SelectedItem).Link))
                {
                    config.HiddenPosts.Add(((Post)grid.SelectedItem).Link);
                    config.SelectedSearchQuery.Posts.Remove((Post)grid.SelectedItem);
                    grid.ItemsSource = config.SelectedSearchQuery.Posts.OrderByDescending(x => x.Date);
                }
            }
        }

        #endregion Grid

        private void Btn_OpenSettings_Click(object sender, RoutedEventArgs e)
        {
            var settingsWindow = new SettingsWindow();
            settingsWindow.DataContext = config.Settings;
            settingsWindow.ShowDialog();
        }

        /// <summary>
        /// Emails Post info to my email address
        /// </summary>
        /// <param name="item"></param>
        private void MailAlert(Post item)
        {
            bool sent = false;
            while (sent == false)
            {
                try
                {
                    int port;
                    bool result = int.TryParse(config.Settings.EmailPort, out port);
                    if (!result)
                    {
                        MessageBox.Show("The Email Port has to be an integer.", "Error");
                        return;
                    }

                    SmtpClient client = new SmtpClient();
                    client.Host = config.Settings.EmailServer;
                    client.Port = port;
                    client.UseDefaultCredentials = false;
                    client.DeliveryMethod = SmtpDeliveryMethod.Network;
                    client.EnableSsl = config.Settings.EmailUseSSL;
                    client.Credentials = new NetworkCredential(config.Settings.EmailUsername, config.Settings.EmailPassword);
                    MailMessage mail = new MailMessage(config.Settings.EmailFromAddress, config.Settings.EmailToAddress);
                    mail.Subject = "CL Alert: " + item.Title;
                    mail.Body = item.Link;
                    client.Send(mail);
                    sent = true;
                }
                catch (Exception ex)
                {
                    if (ex.Message == "Failure sending mail.")
                    {
                        var task = Task.Factory.StartNew(() =>
                        {
                            Thread.Sleep(300000);
                        });
                    }
                    else
                    {
                        MessageBox.Show(ex.Message, "Error sending Email");
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// Timer Event Handler which updates the Search Queries
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_RefreshData(object sender, EventArgs e)
        {
            RunAllQueries();
        }
        
        #region Save/Load

        /// <summary>
        /// Populates the Categories and Locations
        /// </summary>
        private void AddCLData()
        {
            #region Categories

            config.Categories.Add(new Category("ccc", "all community"));
            config.Categories.Add(new Category("eee", "all event"));
            config.Categories.Add(new Category("sss", "all for sale / wanted"));
            config.Categories.Add(new Category("ata", "antiques"));
            config.Categories.Add(new Category("atd", "antiques - by dealer"));
            config.Categories.Add(new Category("atq", "antiques - by owner"));
            config.Categories.Add(new Category("ppa", "appliances"));
            config.Categories.Add(new Category("ppd", "appliances - by dealer"));
            config.Categories.Add(new Category("app", "appliances - by owner"));
            config.Categories.Add(new Category("ard", "arts & crafts - by dealer"));
            config.Categories.Add(new Category("art", "arts & crafts - by owner"));
            config.Categories.Add(new Category("ara", "arts+crafts"));
            config.Categories.Add(new Category("pta", "auto parts"));
            config.Categories.Add(new Category("ptd", "auto parts - by dealer"));
            config.Categories.Add(new Category("pts", "auto parts - by owner"));
            config.Categories.Add(new Category("bad", "baby & kid stuff - by dealer"));
            config.Categories.Add(new Category("bab", "baby & kid stuff - by owner"));
            config.Categories.Add(new Category("baa", "baby+kids"));
            config.Categories.Add(new Category("bar", "barter"));
            config.Categories.Add(new Category("haa", "beauty+hlth"));
            config.Categories.Add(new Category("bid", "bicycles - by dealer"));
            config.Categories.Add(new Category("bik", "bicycles - by owner"));
            config.Categories.Add(new Category("bia", "bikes"));
            config.Categories.Add(new Category("boo", "boats"));
            config.Categories.Add(new Category("bod", "boats - by dealer"));
            config.Categories.Add(new Category("boa", "boats - by owner"));
            config.Categories.Add(new Category("bka", "books"));
            config.Categories.Add(new Category("bkd", "books & magazines - by dealer"));
            config.Categories.Add(new Category("bks", "books & magazines - by owner"));
            config.Categories.Add(new Category("bfa", "business"));
            config.Categories.Add(new Category("bfd", "business/commercial - by dealer"));
            config.Categories.Add(new Category("bfs", "business/commercial - by owner"));
            config.Categories.Add(new Category("ctd", "cars & trucks - by dealer"));
            config.Categories.Add(new Category("cto", "cars & trucks - by owner"));
            config.Categories.Add(new Category("cta", "cars+trucks"));
            config.Categories.Add(new Category("emq", "cds / dvds / vhs - by dealer"));
            config.Categories.Add(new Category("emd", "cds / dvds / vhs - by owner"));
            config.Categories.Add(new Category("ema", "cds/dvd/vhs"));
            config.Categories.Add(new Category("moa", "cell phones"));
            config.Categories.Add(new Category("mod", "cell phones - by dealer"));
            config.Categories.Add(new Category("mob", "cell phones - by owner"));
            config.Categories.Add(new Category("cla", "clothes+acc"));
            config.Categories.Add(new Category("cld", "clothing & accessories - by deal"));
            config.Categories.Add(new Category("clo", "clothing & accessories - by owne"));
            config.Categories.Add(new Category("cba", "collectibles"));
            config.Categories.Add(new Category("cbd", "collectibles - by dealer"));
            config.Categories.Add(new Category("clt", "collectibles - by owner"));
            config.Categories.Add(new Category("sya", "computers"));
            config.Categories.Add(new Category("syd", "computers - by dealer"));
            config.Categories.Add(new Category("sys", "computers - by owner"));
            config.Categories.Add(new Category("ela", "electronics"));
            config.Categories.Add(new Category("eld", "electronics - by dealer"));
            config.Categories.Add(new Category("ele", "electronics - by owner"));
            config.Categories.Add(new Category("grq", "farm & garden - by dealer"));
            config.Categories.Add(new Category("grd", "farm & garden - by owner"));
            config.Categories.Add(new Category("gra", "farm+garden"));
            config.Categories.Add(new Category("ssq", "for sale by dealer"));
            config.Categories.Add(new Category("sso", "for sale by owner"));
            config.Categories.Add(new Category("zip", "free stuff"));
            config.Categories.Add(new Category("fua", "furniture"));
            config.Categories.Add(new Category("fud", "furniture - by dealer"));
            config.Categories.Add(new Category("fuo", "furniture - by owner"));
            config.Categories.Add(new Category("gms", "garage sales"));
            config.Categories.Add(new Category("foa", "general"));
            config.Categories.Add(new Category("fod", "general for sale - by dealer"));
            config.Categories.Add(new Category("for", "general for sale - by owner"));
            config.Categories.Add(new Category("had", "health and beauty - by dealer"));
            config.Categories.Add(new Category("hab", "health and beauty - by owner"));
            config.Categories.Add(new Category("hsa", "household"));
            config.Categories.Add(new Category("hsd", "household items - by dealer"));
            config.Categories.Add(new Category("hsh", "household items - by owner"));
            config.Categories.Add(new Category("wan", "items wanted"));
            config.Categories.Add(new Category("jwa", "jewelry"));
            config.Categories.Add(new Category("jwd", "jewelry - by dealer"));
            config.Categories.Add(new Category("jwl", "jewelry - by owner"));
            config.Categories.Add(new Category("maa", "materials"));
            config.Categories.Add(new Category("mad", "materials - by dealer"));
            config.Categories.Add(new Category("mat", "materials - by owner"));
            config.Categories.Add(new Category("mca", "motorcycles"));
            config.Categories.Add(new Category("mcd", "motorcycles/scooters - by dealer"));
            config.Categories.Add(new Category("mcy", "motorcycles/scooters - by owner"));
            config.Categories.Add(new Category("msa", "music instruments"));
            config.Categories.Add(new Category("msd", "musical instruments - by dealer"));
            config.Categories.Add(new Category("msg", "musical instruments - by owner"));
            config.Categories.Add(new Category("pha", "photo+video"));
            config.Categories.Add(new Category("phd", "photo/video - by dealer"));
            config.Categories.Add(new Category("pho", "photo/video - by owner"));
            config.Categories.Add(new Category("rva", "recreational vehicles"));
            config.Categories.Add(new Category("rvd", "rvs - by dealer"));
            config.Categories.Add(new Category("rvs", "rvs - by owner"));
            config.Categories.Add(new Category("sga", "sporting"));
            config.Categories.Add(new Category("sgd", "sporting goods - by dealer"));
            config.Categories.Add(new Category("spo", "sporting goods - by owner"));
            config.Categories.Add(new Category("tia", "tickets"));
            config.Categories.Add(new Category("tid", "tickets - by dealer"));
            config.Categories.Add(new Category("tix", "tickets - by owner"));
            config.Categories.Add(new Category("tla", "tools"));
            config.Categories.Add(new Category("tld", "tools - by dealer"));
            config.Categories.Add(new Category("tls", "tools - by owner"));
            config.Categories.Add(new Category("tad", "toys & games - by dealer"));
            config.Categories.Add(new Category("tag", "toys & games - by owner"));
            config.Categories.Add(new Category("taa", "toys+games"));
            config.Categories.Add(new Category("vga", "video gaming"));
            config.Categories.Add(new Category("vgd", "video gaming - by dealer"));
            config.Categories.Add(new Category("vgm", "video gaming - by owner"));
            config.Categories.Add(new Category("ggg", "all gigs"));
            config.Categories.Add(new Category("hhh", "all housing"));
            config.Categories.Add(new Category("jjj", "all jobs"));
            config.Categories.Add(new Category("ppp", "all personals"));
            config.Categories.Add(new Category("bbb", "all services offered"));
            config.Categories.Add(new Category("res", "all resume"));

            #endregion Categories

            #region Locations

            try
            {
                if (System.IO.File.Exists("Locations.xml"))
                {
                    System.Xml.Serialization.XmlSerializer reader = new System.Xml.Serialization.XmlSerializer(config.Locations.GetType());
                    System.IO.StreamReader file = new System.IO.StreamReader("Locations.xml");
                    config.Locations = (ObservableCollection<Country>)reader.Deserialize(file);
                    file.Close();
                }
                else
                {
                    MessageBox.Show("The Locations.xml file could not be found. Please reinstall the application!", "Load Locations Error");
                    Environment.Exit(-1);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("There is an issue with Locations.xml file. Please reinstall the application! \n" + ex.Message, "Load Locations Error");
                Environment.Exit(-1);
            }

            #endregion Locations
        }

        private void LoadSettings()
        {
            try
            {
                if (System.IO.File.Exists("Settings.xml"))
                {
                    System.Xml.Serialization.XmlSerializer reader = new System.Xml.Serialization.XmlSerializer(config.GetType());
                    System.IO.StreamReader file = new System.IO.StreamReader("Settings.xml");

                    var locations = config.Locations;
                    var categories = config.Categories;

                    config = (Configuration)reader.Deserialize(file);
                    config.Locations = locations;
                    config.Categories = categories;

                    file.Close();

                    grid.ItemsSource = config.SearchQueries;

                    foreach (SearchQuery item in config.SearchQueries)
                    {
                        item.Posts = new ObservableCollection<Post>();

                        var task = Task.Factory.StartNew(() =>
                        {                            
                            RunQuery(item);
                        });
                    }

                    txt_StatusBar.Content = "Last update: " + DateTime.Now.ToString();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Load Settings Error");
            }
        }

        private void SaveSettings()
        {
            try
            {
                System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(config.GetType());
                System.IO.StreamWriter file = new System.IO.StreamWriter("Settings.xml");
                writer.Serialize(file, config);
                file.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Save Settings Error");
            }
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            SaveSettings();
        }

        #endregion Save/Load

        /// <summary>
        /// Checks for new version of the applications by looking at the Codeplex RSS Feed
        /// </summary>
        private void CheckForUpdates()
        {
            try
            {                
                string rssurl = "http://clsearcher.codeplex.com/project/feeds/rss?ProjectRSSFeed=codeplex://release/clsearcher";
                XmlDocument rssXml = new XmlDocument();
                rssXml.Load(rssurl);

                XmlNodeList rssNodeList = rssXml.SelectNodes("rss/channel/item");

                // Gets the newest rss entries title, link and description
                if (rssNodeList != null)
                {
                    XmlNode rssSubNode = rssNodeList[0].SelectSingleNode("title");
                    string title = rssSubNode != null ? rssSubNode.InnerText : string.Empty;
                    rssSubNode = rssNodeList[0].SelectSingleNode("link");
                    string link = rssSubNode != null ? rssSubNode.InnerText : string.Empty;
                    rssSubNode = rssNodeList[0].SelectSingleNode("description");
                    string desc = rssSubNode != null ? rssSubNode.InnerText : string.Empty;

                    // Strips the HTML code from the RSS Description
                    string strippeddesc = Regex.Replace(desc.Replace("<br />", "\n").Replace("&quot;", "\"").Replace("&#39;", "'"), "<.*?>", string.Empty);

                    // Gets the server and client version strings
                    string server = title.Substring(title.IndexOf(":", StringComparison.Ordinal) + 2, title.IndexOf("(", StringComparison.Ordinal) - title.IndexOf(":", StringComparison.Ordinal) - 3);
                    string client = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();

                    // Converts the version string to a List of integers
                    List<int> lstServer = server.Split('.').ToList().ConvertAll(Convert.ToInt32);
                    List<int> lstClient = client.Split('.').ToList().ConvertAll(Convert.ToInt32);

                    // Check if the RSS version contains a version higher than the client
                    for (int i = 0; i <= 3; i++)
                    {
                        if (lstServer[i] < lstClient[i])
                        {
                            return;
                        }
                        else if (lstServer[i] > lstClient[i])
                        {
                            if (MessageBox.Show(title + "\n" + strippeddesc + "\n\nClick OK to go to the Download Page.", "Update Found!", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                            {
                                System.Diagnostics.Process.Start(link);
                                return;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex.Message == "The remote name could not be resolved: 'clsearcher.codeplex.com'")
                {
                    MessageBox.Show("Unable to check for a new version, you most likely don't have internet access.", "Warning");
                }
            }
        }
    }
}